package eva.savant.business.ventas.service

import eva.savant.business.material.service.TSKardex
import eva.savant.business.ventas.service.util.CodigoControl
import eva.savant.business.contabilidad.service.TSContabilidad
import eva.savant.businessobject.cg.*
import eva.savant.businessobject.gl.*
import eva.savant.businessobject.mt.*
import eva.savant.businessobject.vt.*
import eva.savant.core.service.TSEntity

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Repository
import java.text.SimpleDateFormat

@Repository(value = 'tsVenta')
public class TSVentaImpl extends TSEntity implements TSVenta {

  @Autowired
  TSKardex tsKardex

  @Autowired
  TSContabilidad tsContabilidad

  public Venta saveVenta(Cajero cajero, Almacen almacen, Almacenero o, Venta v) throws Exception { //Flow

    if(v.fecha == null) throw new Exception('La fecha de la venta es invalido')

    sociedadId= v.sociedadId; tsContabilidad.valCredito(v.cliente.persona,v.fecha,v.total)


    def m= tsKardex.saveInKardex( buildMovimiento(almacen,o,v) )

        v= merge(buildVenta(v))

    def c= tsContabilidad.restaCredito(v.cliente.persona,v.total)

    def a= tsContabilidad.mergeAsiento(buildAsiento(v,c.cuenta))

    def d= tsContabilidad.mergeDocumento(buildDocumento(v,c))


    v.asiento= a; v.documento= d; v= merge(v)

    m.documento= v.id; merge(m)

    if(v.pedido!=null) { v.pedido.estado= 'V'; merge(v.pedido) }

    v.cliente.venta= v.fecha; merge(v.cliente)


    mergePagoEfectivo(cajero,v)

    return v
  }

  public Libroventas saveFactura(Dosificacion d, Venta v, String nit, String razon, String documento) throws Exception {

    def libro= merge(buildLibroventas(d,v,nit,razon,documento))

    d.correlativo= d.correlativo + 1; merge(d)

    merge(buildCliente(v,nit,razon))

    merge(new Nit(libro.nit,libro.razonsocial,ALTA))


    return libro
  }

  public Libroventas saveVenta(Cajero c, Almacen a, Almacenero o, Dosificacion d, Venta v, String nit, String razon) throws Exception {

    v= saveVenta(c,a,o,v); return saveFactura(d,v,nit,razon,v.id+'')
  }

  public Venta buildVenta(Venta v) throws Exception {

    if(v.lsVentaDetalle?.isEmpty()) throw new Exception('El detalle de la venta esta vacio o es invalido')

    if(v.vendedor?.id == null) throw new Exception('El vendedor para la venta es invalido')

    if(v.cliente == null) throw new Exception('El cliente para la venta es invalido')

    if(v.cliente.canal == null) throw new Exception('La lista de precio del cliente es invalido')


    v.importe   = 0

    v.impuesto  = 0

    v.descuento = 0

    v.total     = 0


    for(d in v.lsVentaDetalle) {

      def p= get(Precio,'canal.id=? and material.id=?',v.cliente.canal.id,d.material.id)

      if( p?.precio==null) throw new Exception('Precio o producto invalido en el canal '+v.cliente.canal.codigo)

      if( d.cantidad < 0 ) throw new Exception('La cantidad del material '+ d.material.codigo + ' debe ser mayor a cero')


      d.venta       = v

      d.precio      = p.precio

      d.importe     = d.cantidad * d.precio

      d.impuesto    = p?.impuesto > 0? (d.cantidad * p.impuesto) : 0

      d.descuento   = p?.descuento > 0? (d.cantidad * p.descuento) : 0

      d.total       = (d.importe - d.descuento) + d.impuesto

      d.operacionId = v.operacionId

      d.sociedadId  = v.sociedadId

      d.usuarioId   = v.usuarioId


      v.importe  = v.importe   + d.importe

      v.impuesto = v.impuesto  + d.impuesto

      v.descuento= v.descuento + d.descuento

      v.total    = v.total     + d.total
    }

    v.pagado   = v.pagado > 0? v.pagado : v.total

    v.vuelto   = v.pagado - v.total

    return v
  }

  public void mergePagoEfectivo(Cajero c, Venta v) throws Exception {

    if(v.tipo.equalsIgnoreCase('EN') || v.tipo.equalsIgnoreCase('EF')) {

    def d= new DocumentoDetalle()

    d.documento= v.documento

    d.cajero= c

    d.tipo= get(Tipo,v.sociedadId + 'MP1')

    d.referencia= 'Venta ' + v.id

    d.descripcion= 'Pago venta'

    d.importe= v.total

    d.fecha= v.fecha

    d.operacionId= v.documento.operacionId

    d.sociedadId = v.documento.sociedadId

    d.usuarioId  = v.documento.usuarioId


    v.documento.tipo= 'CXC-VNT-EF'

    v.documento.estado= 'C'

    v.documento.saldo= 0

    v.documento.importe= v.total

    v.documento.lsDocumentoDetalle= new ArrayList()

    v.documento.lsDocumentoDetalle.add d


    merge(v.documento)

    tsContabilidad.sumaCredito(v.cliente.persona,v.total) }
  }

  public Libroventas buildLibroventas(Dosificacion d, Venta v, String nit, String razon, String documento) throws Exception { //Se toma la fecha de la venta

    if(d?.id == null) throw new Exception('La dosificacion para la venta es invalido')

    sociedadId= v.sociedadId


    def i= get(Impuesto,'codigo=?',IMPUESTO_IVA)

    if(i?.valor == null) throw new Exception('El impuesto IVA para la venta no esta definido')

    def it= get(Impuesto,'codigo=?',IMPUESTO_IT)

    if(it?.valor == null) throw new Exception('El impuesto IT para la venta no esta definido')


    nit= nit?.length() == 0? '0' : nit

    razon= nit.equals('0')? 'SIN NOMBRE' : razon


    def l          = new Libroventas()

    l.dosificacion = d

    l.venta        = v

    l.documento    = documento != null? documento : v.id

    l.nit          = nit

    l.razonsocial  = razon

    l.factura      = d.correlativo + ''

    l.autorizacion = d.autorizacion

    l.fecha        = v.fecha

    l.importe      = v.total

    l.iva          = v.total * i.valor

    l.it           = v.total * it.valor

    l.estado       = 'V'

    l.limite       = d.limite

    l.codigo       = getCodigoControl(l.autorizacion,
                                      l.factura,
                                      l.nit,
                                      new SimpleDateFormat('yyyyMMdd').format(l.fecha),
                                      l.importe.doubleValue(),
                                      d.llave)

    l.operacionId  = v.operacionId

    l.sociedadId   = v.sociedadId

    l.usuarioId    = v.usuarioId


    return l
  }

  public static Cliente buildCliente(Venta v, nit, razonsocial) {

    v.cliente.razon= razonsocial

    v.cliente.nit= nit

    return v.cliente
  }

  public String getCodigoControl(String autorizacion, String factura, String nit, String fecha, double monto, String llave) throws Exception {

    return CodigoControl.getCodigoControl(autorizacion,factura,nit,fecha,monto,llave)
  }

  public Movimiento buildMovimiento(Almacen a, Almacenero o, Venta v) { //Se toma la fecha de la venta

    def m= new Movimiento()

    m.almacenero= o

    m.motivo= get(Motivo,TSKardex.VENTA)

    m.fecha= v.fecha

    m.documento= v.id

    m.descripcion= 'Venta'

    m.tipo= TSKardex.EGRESO

    m.operacionId= v.operacionId

    m.sociedadId= v.sociedadId

    m.usuarioId= v.usuarioId

    m.lsKardex= new ArrayList<Kardex>()


    for(x in v.lsVentaDetalle) {

    def k= new Kardex()

    k.almacen        = a

    k.persona        = v.cliente.persona

    k.material       = x.material

    k.salidaCantidad = x.cantidad

    m.lsKardex.add k }


    return m
  }

  public void anularVenta(Venta v) throws Exception { //Flow

    sociedadId= v.sociedadId

    anularFacturaLibro(v)


    def lsM= find(Movimiento,'motivo.id=? and documento=?',TSKardex.VENTA,v.id+'')

    for(m in lsM) tsKardex.anularMovimiento(m.id)


    v= get(Venta, v.id); if(v == null) return

    v.operacionId= BAJA; merge(v)


    anularDocumento(v)

    if(v.pedido!=null) { v.pedido.estado= 'A'; merge(v.pedido) }
  }

  public Libroventas anularFacturaLibro(Venta v) throws Exception {

    sociedadId= v.sociedadId

    def l= get(Libroventas,'venta.id=?', v.id)

    if(l == null) return null

    l.nit = '0'

    l.razonsocial= 'ANULADO'

    l.estado= 'A'

    l.codigo= '0'

    l.importe= 0

    l.iva= 0

    l.it= 0

    return merge(l)
  }

  public Documento buildDocumento(Venta v, Credito c) throws Exception {

    def d= v.documento==null? new Documento() : v.documento

    d.estado= 'N'

    d.persona= v.cliente.persona

    d.referencia= 'Venta ' + v.id

    d.descripcion= 'Venta: ' + c.cuenta.nombre

    d.importe= v.total

    d.creacion= v.fecha

    d.vencimiento= d.creacion + (int) c.condpago.valor

    d.tipo= 'CXC-VNT'

    d.operacionId= v.operacionId

    d.sociedadId= v.sociedadId

    d.usuarioId= v.usuarioId

    return d
  }

  public void anularDocumento(Venta v) throws Exception { // Flow ver con anular venta

    if(v?.cliente == null || v?.documento == null) return

    tsContabilidad.sumaCredito(v.cliente.persona,v.total)

    tsContabilidad.anularDocumento(v.documento)
  }

  public Asiento buildAsiento(Venta v, Cuenta c) throws Exception {

    def preasiento= get(Preasiento,v.tipo.equals('CN')   ||   v.tipo.equals('EN')?

                                   PREASIENTO_VENTA_NOTA : PREASIENTO_VENTA_FACT)

    def x= v.asiento==null? new Asiento() : v.asiento

    x.tipo       = preasiento.tipo

    x.fecha      = v.fecha

    x.detalle    = 'Por ventas segun registro'

    x.importe    = v.total

    x.referencia = 'VENTA ' + v.id

    x.operacionId= v.operacionId

    x.sociedadId = v.sociedadId

    x.usuarioId  = v.usuarioId

    x.lsPartida= new ArrayList<Partida>()


    def band= true; preasiento.lsPreasientoDetalle.each { println 'preasiento venta cuenta: '+it.cuenta.nombre

    def p= new Partida()

    p.cuenta= band && (v.tipo.equals('CN') || v.tipo.equals('CF'))? c : it.cuenta; band= false

    p.debe  = it.grupo.equalsIgnoreCase('DEBE')?  (it.porcentaje/100) * x.importe : 0

    p.haber = it.grupo.equalsIgnoreCase('HABER')? (it.porcentaje/100) * x.importe : 0

    x.lsPartida.add p }


    return x
  }

}